//! 缓存控制器相关模块
//!
//!

use crate::{pages::{RawPageData, Page}, disk::DiskControl, types::{FullPageID, PageID, RelationID}, konst::PAGE_SIZE};
use std::{sync::{Arc, atomic::{AtomicUsize, AtomicU32}}, collections::HashMap, mem::ManuallyDrop};
use parking_lot::RwLock;
use std::alloc::Layout;

use super::FrameID;


/// 页框元数据
pub struct FrameMeta {
    /// 页框内页面的ID
    page_id: FullPageID,

    /// 页框的引用计数
    ref_count: AtomicUsize,

    /// 页框内容是否待写回磁盘
    dirty: bool,

    /// 实际的页框
    frame: RwLock<std::mem::ManuallyDrop<Box<RawPageData>>>
}

/// 缓存控制器, 负责缓存页面的控制
pub struct BufferControl {
    /// 磁盘控制器
    disk: Arc<DiskControl>,

    /// 页面映射表
    page_map: HashMap<FullPageID, FrameID>,

    /// 所有的页面
    frames: Box<[FrameMeta]>,

    buffer_layout: Layout,
    buffer_ptr: *mut u8,
}

impl Drop for BufferControl {
    fn drop(&mut self) {
        // 将所有的脏页全部刷回磁盘
        for frame in self.frames.iter() {
            if frame.dirty {
                let mem = frame.frame.try_read()
                    .expect("page locked before BufferControl destruct");
                self.disk.write_page_to(frame.page_id, &mem);
            }
        }

        // release buffer pool memory
        unsafe {
            std::alloc::dealloc(self.buffer_ptr, self.buffer_layout);
        }
    }
}

impl BufferControl {

    pub fn new(frame_count: usize, disk: Arc<DiskControl>) -> Self {
        let buffer_layout = Layout::from_size_align(frame_count * PAGE_SIZE, PAGE_SIZE)
            .expect("create buffer layout failed");
        
        let buffer_ptr = unsafe {
            std::alloc::alloc(buffer_layout)
        };
        let mut frames = Vec::with_capacity(frame_count);

        for i in 0 .. frame_count {
            let array = unsafe {
                let shard: *mut RawPageData = std::mem::transmute(buffer_ptr.add(PAGE_SIZE * i));
                ManuallyDrop::new(Box::from_raw(shard))
            };
            let frame = RwLock::new(array);
            frames.push(FrameMeta {
                page_id: FullPageID::INVALID,
                ref_count: 0.into(),
                dirty: false,
                frame,
            });
        }

        Self {
            disk,
            page_map: Default::default(),
            frames: frames.into_boxed_slice(),
            buffer_layout,
            buffer_ptr,
        }
    }

    /// 在指定的表或关系文件中分配一个新页面, 并返回这个页面.
    pub fn new_page(relation: RelationID) -> Page {
        todo!()
    }

    /// 从指定的表或关系文件中按照页面ID, 查询出一个页面并返回它.
    pub fn fetch_page(page_id: FullPageID) -> Option<Page> {
        todo!()
    }

    /// 删除页面, 按照页面ID删除页面.
    /// 返回是否删除成功.
    pub fn delete_page(page_id: FullPageID) -> bool {
        todo!()
    }

}


#[cfg(test)]
mod test {
    use tempfile::tempdir;
    use super::*;

    #[test]
    fn create_control_create_drop_smoke_test() {
        let dir = tempdir().expect("gen temp dir failed");
        let disk_control = DiskControl::new(dir.path().to_path_buf());
        let buffer_control = BufferControl::new(100, Arc::new(disk_control));
        drop(buffer_control)
    }
}